package missObjects;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.logging.Logger;

import db.RawData;
import main.Prop;

/**
 * Handle Aifield writing in file and DB and reading form DB. Methods and fields
 * extended from RofMissionObject
 * 
 * @author lefuneste
 *
 */
public class RofMissObjAirfield extends RofMissionObject {
	// additionnal datas
	private double[] LandingPointInfos;
	private boolean filtered;
	private double XLP;
	private double ZLP;
	private double oriLP;

	// used in methods
	private static int writeInDBCount = 0;
	
	//getter and setters
	public double getXLP() {
		return XLP;
	}

	public void setXLP(double xLP) {
		XLP = xLP;
	}

	public double getZLP() {
		return ZLP;
	}

	public void setZLP(double zLP) {
		ZLP = zLP;
	}

	public double getOriLP() {
		return oriLP;
	}

	public void setOriLP(double oriLP) {
		this.oriLP = oriLP;
	}

	//logging
	private static Logger LOGGER = Logger.getLogger("RofLogger");
	static {
		LOGGER.getUseParentHandlers();
	}


	/**
	 * Create a empty Airfield object
	 */
	public RofMissObjAirfield() {
		super();
		this.LandingPointInfos = new double[3];
		this.objectType = "Airfield";
		this.filtered = false;
	}

	/**
	 * Create an Airfield from a template object previously read in a file and
	 * filtered information from DB information
	 * 
	 * @param template
	 *            : object read in file
	 * @throws SQLException
	 */
	public RofMissObjAirfield(RofTemplateObject template, Connection con)
			throws SQLException {
		super();
		// common part
		this.objectType = template.objectType;
		this.indexInfile = template.indexInfile;
		this.mapID = template.mapID;
		this.fileID = template.fileID;
		this.name = template.name;
		this.groupID = template.groupID;
		// standard part
		this.Xpos = template.Xpos;
		this.Ypos = template.Ypos;
		this.Zpos = template.Zpos;
		this.Xori = template.Xori;
		this.Yori = template.Yori;
		this.Zori = template.Zori;
		this.model = template.model;
		// specific fields
		this.LandingPointInfos = new double[3];
		this.filtered = true;
		for (RawData data : template.unProcessedData) {
			this.unProcessedData.add(data);
		}

		// retrieve the infos for the model
		this.retrieveModelInfo(con);
	}

	/**
	 * Set landing point coordinates and filtered status by retrieving it in DB
	 * from the model of the airfield
	 * 
	 * @param con
	 *            connection to DB
	 * @throws SQLException
	 */
	public void retrieveModelInfo(Connection con) throws SQLException {
		Double lPX;
		Double lPZ;
		Double lPAngle;
		boolean firstLine = true;

		// ex: select * from AIRFIELDDATA as D LEFT OUTER JOIN (SELECT * from
		// AIRFIELDFILTER where (fileID=-1 and AirfieldID=-1) or (fileID=0 and
		// AirfieldID=1466)) as F on D.NAME=F.name WHERE
		// D.NAME='"bertangles_01"'
		// Landing point info will always be in the query results
		// if the airfield model must always be used, filter datas will be null
		// if the airfield model must be ignored for all airfield, filter
		// without exception, datas will be -1
		// if the airfield model is generally ignored but not for this airfield
		// there will be 2 results rows: one for all airfield and one for the
		// current airfield

		ResultSet results = null;
		try (Statement stmt = con.createStatement()) {
			String sql = "select * from AIRFIELDDATA as D LEFT OUTER JOIN (SELECT * from AIRFIELDFILTER where (fileID=-1 and AirfieldID=-1) or ("
					+ "fileID="
					+ this.fileID
					+ " AND AirfieldID="
					+ this.indexInfile
					+ ")) as F"
					+ " on D.NAME=F.name WHERE D.NAME='"
					+ this.model
					+ "'"
					+ " ;";
			results = stmt.executeQuery(sql);
			while (results.next()) {
				// there may be two results lines

				lPX = results.getDouble("TAKEOFFDX");
				lPZ = results.getDouble("TAKEOFFDZ");
				lPAngle = results.getDouble("TAKEOFFDOY");

				// define the absolute LP position regarding the position of the
				// Airfield
				this.LandingPointInfos[0] = Xpos
						+ Math.cos(Math.toRadians(this.Yori)) * lPX
						+ Math.cos(Math.toRadians(this.Yori + 90)) * lPZ;
				this.LandingPointInfos[1] = Zpos
						+ Math.sin(Math.toRadians(this.Yori)) * lPX
						+ Math.sin(Math.toRadians(this.Yori + 90)) * lPZ;
				this.LandingPointInfos[2] = Yori + lPAngle;

				if (firstLine == true) {
					// if there is only one line, the airfieldmodel is always
					// used (null value for filter) or always ignored (-1 value
					// for filter)
					firstLine = false;
					if (results.getInt("fileID") == -1) {
						// always ignored
						this.filtered = true;
					} else {
						this.filtered = false;
					}

				} else {
					// if there is a second line the airfield must not be
					// ignored
					this.filtered = false;
				}
			}
		} catch (Exception e) {
			LOGGER.severe(e.getClass().getName() + ": " + e.getMessage());
			throw (e);
		}
	}

	/**
	 * Store the Airfield into DB
	 * 
	 * @param con
	 *            connection to DB
	 * @throws SQLException
	 */
	public void storeToDB(Connection con) throws SQLException {
		// int autoIncKey = 0;
		try (Statement stmt = con.createStatement()) {

			String sql = "INSERT OR IGNORE INTO "
					+ Prop.get("AIRFIELD")
					+ " (name, fileID, groupID, indexInFile, fileID, mapID, Xpos, Ypos, Zpos, Xori, Yori, Zori, model, XLP, ZLP, OriLP, filtered ) VALUES('"
					+ this.name + "','" + this.fileID + "','" + this.groupID
					+ "','" + this.indexInfile + "','" + this.fileID + "','"
					+ this.mapID + "','" + this.Xpos + "','" + this.Ypos
					+ "','" + this.Zpos + "','" + this.Xori + "','" + this.Yori
					+ "','" + this.Zori + "','" + this.model + "','"
					+ this.LandingPointInfos[0] + "','"
					+ this.LandingPointInfos[1] + "','"
					+ this.LandingPointInfos[2] + "','" + this.filtered + "');";
			stmt.executeUpdate(sql);
			writeInDBCount++;

			// storage of associated non processed data

			for (int i = 0; i < unProcessedData.size(); i++) {
				sql = "INSERT OR IGNORE INTO " + Prop.get("RAWDATA")
						+ " (id, objID, fileID, RawData ) VALUES('"
						+ this.unProcessedData.get(i).getOrderNum()+"','" +this.indexInfile + "','" + this.fileID + "','"
						+ this.unProcessedData.get(i).getLine() + "');";
				stmt.addBatch(sql);
			}
			stmt.executeBatch();

			LOGGER.fine("Airfield " + this.name + "written into DB, "
					+ unProcessedData.size()
					+ " associated lines added into RawData");
			stmt.close();

		} catch (Exception e) {
			LOGGER.severe(e.getClass().getName() + ": " + e.getMessage());
			throw (e);
		}

	}
	
	//TODO : UPDATE OF AIRFIELDS
	/* 	
	 	UPDATE airfield
		SET airfield.XLP = airfieldfix.XLP
		FROM airfield
		JOIN airfieldfix
		ON airfieldfix.fileID = airfield.fileID AND airfieldfix.indexInfile = airfield.indexInfile
		
		WHERE NOT airfieldfix.fileID IS NULL
		
		ou
		
		UPDATE Airfield SET XLP = 31299.17, ZLP = 284776.4, OriLP = 207 WHERE (XLP = 31291.676248091146) AND (ZLP = 284878.921227994) AND (OriLP = 163.137)
*/

	/**
	 * Retrieve a Airfield object from the database by using its IndexInFile and
	 * fileID
	 * 
	 * @param con
	 *            : connection to DB
	 * @param IndexInFile
	 *            : ref#1 of Block
	 * @param fileID
	 *            : ref#2 of Block
	 * @return
	 * @throws SQLException
	 */
	public void readFromDB(Connection con, int indexInFile, int fileID)
			throws SQLException {

		ResultSet results1 = null;
		ResultSet results2 = null;
		// RofMissObjBlock resultObj=null;

		try (Statement stmt = con.createStatement()) {
			String sql = "SELECT * from " + Prop.get("AIRFIELD")
					+ " WHERE fileID=" + fileID + " AND IndexInFile="
					+ indexInFile + " ;";

			results1 = stmt.executeQuery(sql);

			Statement stmt2 = con.createStatement();
			sql = "SELECT * from " + Prop.get("RAWDATA") + " WHERE fileID="
					+ fileID + " AND objID=" + indexInFile + " ORDER BY id;";
			results2 = stmt2.executeQuery(sql);

			this.readfromResultSet(results1, results2);

		} catch (Exception e) {
			LOGGER.severe(e.getClass().getName() + ": " + e.getMessage());
			throw (e);
		}
	}

	/**
	 * Retrieve a Airfield object from records in DB
	 * 
	 * @param reqResultsblock
	 *            results of a DB request for the block
	 * @param reqResulsraw
	 *            results of a DB request for the associated raw data
	 * @throws SQLException
	 */
	public void readfromResultSet(ResultSet reqResultsblock,
			ResultSet reqResulsraw) throws SQLException {
		RawData rawLine=null;
		try {
			// block info retrieval
			while (reqResultsblock.next()) {
				// should only be one, in other case only the last will be
				// returned
				this.objectType = "Airfield";
				this.indexInfile = reqResultsblock.getInt("indexInFile");
				this.mapID = reqResultsblock.getString("mapID");
				this.fileID = reqResultsblock.getInt("fileID");
				this.name = reqResultsblock.getString("name");
				this.groupID = reqResultsblock.getInt("groupID");
				this.Xpos = reqResultsblock.getDouble("Xpos");
				this.Ypos = reqResultsblock.getDouble("Ypos");
				this.Zpos = reqResultsblock.getDouble("Zpos");
				this.Xori = reqResultsblock.getDouble("Xori");
				this.Yori = reqResultsblock.getDouble("Yori");
				this.Zori = reqResultsblock.getDouble("Zori");
				this.model = reqResultsblock.getString("model");
				this.XLP = reqResultsblock.getDouble("XLP");
				this.ZLP = reqResultsblock.getDouble("ZLP");
				this.oriLP = reqResultsblock.getDouble("oriLP");
				
			}
			// raw data info retrieval
			while (reqResulsraw.next()) {
				String line=reqResulsraw.getString("RawData");
				int rawDataIndex = reqResulsraw.getInt("id");
				rawLine = new RawData(rawDataIndex, line);
				this.unProcessedData.add(rawLine);
			}
		} catch (Exception e) {
			LOGGER.severe(e.getClass().getName() + ": " + e.getMessage());
			throw (e);
		}
	}

	public String toString() {
		String results = "";
		results = this.name + ";" + this.indexInfile + ";" + this.fileID + ";"
				+ this.mapID;
		return results;
	}

}
